package com.example.fireassistant_server.service.impl;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.fireassistant_server.config.SecurityConfig;
import com.example.fireassistant_server.dto.user.*;
import com.example.fireassistant_server.entity.*;
import com.example.fireassistant_server.enums.RoleType;
import com.example.fireassistant_server.exception.BizException;
import com.example.fireassistant_server.exception.ExceptionType;
import com.example.fireassistant_server.mapper.UserMapper;
import com.example.fireassistant_server.repository.UserRepository;
import com.example.fireassistant_server.repository.UserRoleRepository;
import com.example.fireassistant_server.repository.WechatUserRepository;
import com.example.fireassistant_server.repository.spec.SearchCriteria;
import com.example.fireassistant_server.repository.spec.SearchOperation;
import com.example.fireassistant_server.repository.spec.UserSpecification;
import com.example.fireassistant_server.service.CompanyService;
import com.example.fireassistant_server.service.FileService;
import com.example.fireassistant_server.service.UserService;
import com.example.fireassistant_server.service.WechatUserService;
import com.example.fireassistant_server.vo.UserVo;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import java.util.*;


@Service
public class UserServiceImpl implements UserService {

    UserRepository repository;

    UserRoleRepository userRoleRepository;

    FileService fileService;

    UserMapper mapper;

    WechatUserRepository wechatUserRepository;
    PasswordEncoder passwordEncoder;

    private EntityManager entityManager;

    CompanyService companyService;

    @Override
    @Transactional
    public UserDto create(UserCreateRequest userCreateDto) {
        checkUserName(userCreateDto.getUsername());
        User user = mapper.createEntity(userCreateDto);
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        // 添加归属单位 先将one整个对象设置为属性
        Company company = companyService.get(userCreateDto.getCompanyId());
        user.setCompany(company);
        UserDto userDto = mapper.toDto(repository.save(user));
        //创建默认权限
        UserRole userRole = new UserRole();
        userRole.setRoleId(RoleType.ROLE_USER.getCode());
        userRole.setUserId(userDto.getId());
        userRoleRepository.save(userRole);

        // 缓存
        repository.flush();
        entityManager.unwrap(Session.class).clear();
        return mapper.toDto(get(userDto.getId()));
    }


    //更新角色
    @Override
    public User loadUserByUsername(String username) {
        Optional<User> user = repository.findByUsername(username);
        if (!user.isPresent()) {
            throw new BizException(ExceptionType.USER_NOT_FOUND);
        }
        return user.get();
    }

    @Override
    public User get(String id) {
//        User user = repository.getById(id);  若用户不存在，会抛出异常，导致下面判断无法进行
//        if (user == null) {
//            throw new BizException(ExceptionType.USER_NOT_FOUND);
//
        Optional<User> user = repository.findById(id);
        if (!user.isPresent()) {
            throw new BizException(ExceptionType.USER_NOT_FOUND);
        }
        return user.get();
    }


    @Override
    @Transactional
    public UserDto update(String userId, UserUpdateRequest userUpdateRequest) {
        Optional<User> user = repository.findById(userId);
        if (!user.isPresent()) {
            throw new BizException(ExceptionType.USER_NOT_FOUND);
        }
        // userUpdateRequest中的role没有id,无法使用级联操作
        if (userUpdateRequest.getUsername()!= null){
            user.get().setUsername(userUpdateRequest.getUsername());
        }
        if (userUpdateRequest.getFileId()!= null){
            user.get().setFile(fileService.getById(userUpdateRequest.getFileId()));
        }
        if (userUpdateRequest.getApplyAdmin() != null){
            user.get().setApplyAdmin(userUpdateRequest.getApplyAdmin());
        }
        if (userUpdateRequest.getNickname()!= null){
            user.get().setNickname(userUpdateRequest.getNickname());
        }
        if (userUpdateRequest.getPhoneNumber()!=null){
            user.get().setPhoneNumber(userUpdateRequest.getPhoneNumber());
        }
        if (userUpdateRequest.getGender()!=null) {
            user.get().setGender(userUpdateRequest.getGender());
        }
        if (userUpdateRequest.getCompanyId()!= null) {
            // 添加归属单位 先将one整个对象设置为属性
            Company company = companyService.get(userUpdateRequest.getCompanyId());
            user.get().setCompany(company);
        }
        if (userUpdateRequest.getRoles() != null && userUpdateRequest.getRoles().size() !=0 ) {
            List<Role> roles = userUpdateRequest.getRoles();
            userRoleRepository.deleteByUserId(userId);
            // 更新user权限需取消权限申请
            user.get().setApplyAdmin(false);
            for (Role role : roles){
                UserRole userRole = new UserRole();
                userRole.setUserId(userId);
                switch (role.getName()){
                    case "ROLE_USER":
                        userRole.setRoleId(RoleType.ROLE_USER.getCode());
                        userRoleRepository.save(userRole);
                        break;
                    case "ROLE_ADMIN":
                        userRole.setRoleId(RoleType.ROLE_ADMIN.getCode());
                        userRoleRepository.save(userRole);
                        userUpdateRequest.setApplyAdmin(false);
                        break;
                    case "ROLE_STATION_ADMIN":
                        userRole.setRoleId(RoleType.ROLE_STATION_ADMIN.getCode());
                        System.out.println("ROLE_STATION_ADMIN");
                        System.out.println(RoleType.ROLE_STATION_ADMIN.getCode());
                        userRoleRepository.save(userRole);
                        break;
                    case "ROLE_SUPER_ADMIN":
                        userRole.setRoleId(RoleType.ROLE_SUPER_ADMIN.getCode());
                        userRoleRepository.save(userRole);
                        break;
                }

            }
        }
        repository.flush();
        userRoleRepository.flush();
        entityManager.unwrap(Session.class).clear();
        return mapper.toDto(repository.save(user.get()));
    }

    @Override
    @Transactional
    public void delete(String id) {
        Optional<User> user = repository.findById(id);
        if (!user.isPresent()) {
            throw new BizException(ExceptionType.USER_NOT_FOUND);
        }

//        不能直接使用wechatUserService.getWechatUserByUser(user.get())!=null 进行判断，会导致非微信用户删除异常
        Optional<WeChatUser> weChatUser = wechatUserRepository.getWeChatUserByUser(user.get());
        if (weChatUser.isPresent()) {
            wechatUserRepository.deleteByUserId(user.get().getId());
        }
        repository.deleteById(id);
        if (user.get().getFile()!=null){
            fileService.deleteById(user.get().getFile().getId());
        }

    }

    @Override
    public Page<UserDto> search(UserSearchFilter userSearchFilter) {
        UserSpecification specs = new UserSpecification();
        // ToDo: 需要重构
        if (userSearchFilter.getSource() != null && !Objects.equals(userSearchFilter.getSource(), "")) {
            specs.add(new SearchCriteria("source", userSearchFilter.getSource(), SearchOperation.MATCH));
        }
        //companyId为某值的所有User
        if (userSearchFilter.getCompanyId() !=null && !Objects.equals(userSearchFilter.getCompanyId(), "")){
            Company company = companyService.get(userSearchFilter.getCompanyId());
            specs.add(new SearchCriteria("company",company, SearchOperation.EQUAL));
        }
        //companyId为某值的所有User
        if (userSearchFilter.getUsername() !=null && !Objects.equals(userSearchFilter.getUsername(), "")){
            specs.add(new SearchCriteria("username",userSearchFilter.getUsername(),SearchOperation.MATCH));
        }
        return repository.findAll(specs, userSearchFilter.toPageable()).map(mapper::toDto);
    }

    @Override
    public String createToken(TokenCreateRequest tokenCreateRequest) {
        User user = loadUserByUsername(tokenCreateRequest.getUsername());
        if (!Objects.equals(tokenCreateRequest.getPassword(), user.getPassword())){
            if (!passwordEncoder.matches(tokenCreateRequest.getPassword(), user.getPassword())) {
                throw new BizException(ExceptionType.USER_PASSWORD_NOT_MATCH);
            }
        }
        if (!user.isEnabled()) {
            throw new BizException(ExceptionType.USER_NOT_ENABLED);
        }
        if (!user.isAccountNonLocked()) {
            throw new BizException(ExceptionType.USER_LOCKED);
        }
        return JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() + SecurityConfig.EXPIRATION_TIME))
                .sign(Algorithm.HMAC512(SecurityConfig.SECRET));
    }
    @Override
    public UserDto getCurrentUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User currentUser = loadUserByUsername(authentication.getName());
        return mapper.toDto(currentUser);
    }

    //提升用户权限
    @Override
    public UserDto elevate(String userId) {
        Optional<User> user = repository.findById(userId);
        if (!user.isPresent()) {
            throw new BizException(ExceptionType.USER_NOT_FOUND);
        }
        user.get().setApplyAdmin(true);
        return mapper.toDto(repository.save(user.get()));
    }

    // TODO UserVo
    @Override
    public List<UserVo> getByCompanyId(String id) {
        List<User> userList = repository.findByCompany_Id(id);
        List<UserVo> userVoList = new ArrayList<>();
        if (!userList.isEmpty()){
            userList.forEach(item -> {
                userVoList.add(mapper.toVo(mapper.toDto(item)));
            });
        }
        return userVoList;
    }

    private void checkUserName(String username) {
        Optional<User> user = repository.findByUsername(username);
        if (user.isPresent()) {
            throw new BizException(ExceptionType.USER_NAME_DUPLICATE);
        }
    }


    @Autowired
    private void setRepository(UserRepository repository) {
        this.repository = repository;
    }

    @Autowired
    private void setMapper(UserMapper mapper) {
        this.mapper = mapper;
    }

    @Autowired
    private void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Autowired
    public void setFileService(FileService fileService) {
        this.fileService = fileService;
    }

    @Autowired
    public void setCompanyService(CompanyService companyService) {
        this.companyService = companyService;
    }

    @Autowired
    public void setUserRoleRepository(UserRoleRepository userRoleRepository) {
        this.userRoleRepository = userRoleRepository;
    }

    @Autowired
    public void setWechatUserRepository(WechatUserRepository wechatUserRepository) {
        this.wechatUserRepository = wechatUserRepository;
    }

    @Autowired
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }
}
